package com.franklin.ideaplugin.api.utils.psi;

import cn.hutool.core.util.ArrayUtil;
import com.franklin.ideaplugin.api.beans.declaration.IDeclaration;
import com.franklin.ideaplugin.api.beans.declaration.impl.DeclarationStatementDeclaration;
import com.franklin.ideaplugin.api.beans.declaration.impl.FieldDeclaration;
import com.franklin.ideaplugin.api.beans.declaration.impl.MethodCallExpressionDeclaration;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;

/**
 * psi声明工具类
 *
 * @author Ye Junhui
 * @since 2024/3/5
 */
public class PsiDeclarationUtils {

    /**
     * 寻找变量声明语句
     * @param psiExpression
     * @return
     */
    @Nullable
    public static IDeclaration findExpressionDeclaration(@Nullable PsiElement psiExpression){
        if (Objects.isNull(psiExpression)){
            return null;
        }
        
        //方法对象
        if (psiExpression instanceof PsiMethodCallExpression){
            PsiMethodCallExpression psiMethodCallExpression = (PsiMethodCallExpression) psiExpression;
            return findExpressionDeclarationInMethodCallExpression(psiMethodCallExpression);
        }

        //真正的对象
        PsiIdentifier psiIdentifier = PsiTreeUtil.getChildOfType(psiExpression, PsiIdentifier.class);
        if (Objects.isNull(psiIdentifier)){
            return null;
        }

        //带this，从类中寻找
        PsiThisExpression psiThisExpression = PsiTreeUtil.getChildOfType(psiExpression, PsiThisExpression.class);
        if (Objects.nonNull(psiThisExpression)){
            return findIdentifierDeclarationInClass(psiIdentifier);
        }

        //lambda表达式
        PsiLambdaExpression psiLambdaExpression = PsiTreeUtil.getParentOfType(psiExpression, PsiLambdaExpression.class);
        IDeclaration expressionDeclarationInLambdaExpression = findExpressionDeclarationInLambdaExpression(psiIdentifier, psiLambdaExpression);
        if (Objects.nonNull(expressionDeclarationInLambdaExpression)){
            return expressionDeclarationInLambdaExpression;
        }
        
        //for each循环
        PsiForeachStatement psiForeachStatement = PsiTreeUtil.getParentOfType(psiExpression, PsiForeachStatement.class);
        IDeclaration expressionDeclarationInForEachStatement = findExpressionDeclarationInForEachStatement(psiIdentifier, psiForeachStatement);
        if (Objects.nonNull(expressionDeclarationInForEachStatement)){
            return expressionDeclarationInForEachStatement;
        }

        //当前代码块
        PsiCodeBlock psiCodeBlock = PsiTreeUtil.getParentOfType(psiExpression, PsiCodeBlock.class);

        //一般代码
        return findIdentifierDeclarationInCodeBlock(psiIdentifier,psiCodeBlock);
    }

    /**
     * 从for each中寻找定义
     * @param psiIdentifier
     * @param psiForeachStatement
     * @return
     */
    public static IDeclaration findExpressionDeclarationInForEachStatement(PsiIdentifier psiIdentifier,PsiForeachStatement psiForeachStatement){
        if (Objects.isNull(psiForeachStatement)){
            return null;
        }
        //迭代变量
        PsiParameter iterationParameter = psiForeachStatement.getIterationParameter();
        if (iterationParameter.getName().equals(psiIdentifier.getText())){
            return findExpressionDeclaration(psiForeachStatement.getIteratedValue());
        }
        return null;
    }

    /**
     * 从lambda表达式中寻找
     * @param psiIdentifier
     * @param psiLambdaExpression
     * @return
     */
    public static IDeclaration findExpressionDeclarationInLambdaExpression(PsiIdentifier psiIdentifier, PsiLambdaExpression psiLambdaExpression){
        if (Objects.isNull(psiLambdaExpression)){
            return null;
        }
        //参数
        String targetText = psiIdentifier.getText();
        //寻找参数
        PsiParameterList psiParameterList = psiLambdaExpression.getParameterList();
        PsiParameter targetParameter = ArrayUtil.firstMatch(
                psiParameter -> psiParameter.getName().equals(targetText),
                psiParameterList.getParameters()
        );
        if (Objects.isNull(targetParameter)){
            return null;
        }

        //寻找lambda表达式的最初调用
        PsiMethodCallExpression psiMethodCallExpression = PsiTreeUtil.getParentOfType(psiLambdaExpression, PsiMethodCallExpression.class);
        if (Objects.isNull(psiMethodCallExpression)){
            return null;
        }
        //找到父对象
        PsiIdentifier parentIdentifier = PsiTreeUtil.findChildOfType(psiMethodCallExpression, PsiIdentifier.class);
        if (Objects.isNull(parentIdentifier)){
            return null;
        }
        //寻找父对象表达式
        PsiReferenceExpression parentReferenceExpression = PsiTreeUtil.getParentOfType(parentIdentifier, PsiReferenceExpression.class);

        //如果父表达式为方法调用
        if (Objects.nonNull(parentReferenceExpression)) {
            PsiElement parentReferenceExpressionParent = parentReferenceExpression.getParent();
            if (parentReferenceExpressionParent instanceof PsiMethodCallExpression){
                return findExpressionDeclaration((PsiExpression) parentReferenceExpressionParent);
            }
        }

        //递归寻找
        return findExpressionDeclaration(parentReferenceExpression);
    }

    /**
     * 从方法中寻找变量定义
     * @param psiMethodCallExpression
     * @return
     */
    public static IDeclaration findExpressionDeclarationInMethodCallExpression(PsiMethodCallExpression psiMethodCallExpression){
        if (Objects.isNull(psiMethodCallExpression)){
            return null;
        }
        return new MethodCallExpressionDeclaration(psiMethodCallExpression);
    }

    /**
     * 从代码块中寻找变量声明语句
     * @param psiIdentifier
     * @param psiCodeBlock
     * @return
     */
    @Nullable
    public static IDeclaration findIdentifierDeclarationInCodeBlock(
            PsiIdentifier psiIdentifier,
            @Nullable PsiCodeBlock psiCodeBlock
    ){
        //没有代码块，从类成员中寻找
        if (Objects.isNull(psiCodeBlock)){
            return findIdentifierDeclarationInClass(psiIdentifier);
        }
        //代码行
        for (PsiStatement psiStatement : psiCodeBlock.getStatements()) {
            if (psiStatement instanceof PsiDeclarationStatement){
                PsiDeclarationStatement psiDeclarationStatement = (PsiDeclarationStatement) psiStatement;
                PsiElement[] declaredElements = psiDeclarationStatement.getDeclaredElements();
                for (PsiElement declaredElement : declaredElements) {
                    if (declaredElement instanceof PsiLocalVariable){
                        PsiLocalVariable psiLocalVariable = (PsiLocalVariable) declaredElement;
                        if (psiLocalVariable.getName().equals(psiIdentifier.getText())){
                            return new DeclarationStatementDeclaration(psiDeclarationStatement);
                        }
                    }
                }
            }
        }
        //从父代码块中寻找
        PsiCodeBlock psiParentCodeBlock = PsiTreeUtil.getParentOfType(psiCodeBlock, PsiCodeBlock.class);
        return findIdentifierDeclarationInCodeBlock(psiIdentifier,psiParentCodeBlock);
    }

    /**
     * 从类中寻找声明
     * @param psiIdentifier
     * @return
     */
    public static IDeclaration findIdentifierDeclarationInClass(PsiIdentifier psiIdentifier){
        PsiClass psiClass = PsiTreeUtil.getParentOfType(psiIdentifier, PsiClass.class);
        if (Objects.isNull(psiClass)){
            return null;
        }
        for (PsiField psiField : psiClass.getAllFields()) {
            if (psiField.getName().equals(psiIdentifier.getText())){
                return new FieldDeclaration(psiField);
            }
        }
        return null;
    }

}
